package com.feng.classloader;

import com.feng.util.IOUtils;
import com.sun.javafx.binding.StringFormatter;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Objects;

/**
 * 自定义类加载器
 *  : 在文件系统中加载指定的类
 * @author Lee
 */
public class FileSystemClassLoader extends ClassLoader {

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    /**
     * 文件根路径
     */
    private String rootDir;

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> loadedClass = this.findLoadedClass(name);

        // 如果已经加载了类，则返回被加载的类
        if (loadedClass != null) {
            return loadedClass;
        }

        // 交给 AppClassLoader 类加载器加载，若加载到了，则返回被加载的类
        ClassLoader parentClassLoader = this.getParent();
        try {
            loadedClass = parentClassLoader.loadClass(name);
        } catch (ClassNotFoundException e) {
            System.out.println(MessageFormat.format("AppClassLoader加载 {0} 失败",name));
        }
        if (loadedClass != null) {
            return loadedClass;
        }

        // 再指定目录加载 class 文件，若没有加载到，则抛出 ClassNotFoundException 异常
        byte[] classData = getClassDate(name);
        if (Objects.isNull(classData)) {
            throw new ClassNotFoundException();
        }

        // 调用 抽象类加载器的 defineClass 方法加载类
        loadedClass = this.defineClass(name, classData, 0, classData.length);
        return loadedClass;
    }

    /**
     * 获取指定目录的类字节数组
     * @param name
     * @return
     */
    private byte[] getClassDate(String name) {
        String classPath = rootDir + "/" + name.replace(".","/") + ".class";
        InputStream is = null;
        ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(classPath);

            byte[] buffer = new byte[1024];
            int temp = 0;
            while ((temp = is.read(buffer)) != -1) {
                byteOs.write(buffer,0,temp);
            }
            return byteOs.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            IOUtils.release(is);
            IOUtils.release(byteOs);
        }
    }
}
